/// <reference path="./Maths.ts" />
/// <reference path="./Sets.ts" />
namespace apeng {

	/**
	 * Object 二次封装
	 * 优化增删改查操作
	 *  key 只能是 number | string
	 * @export
	 * @class Maps
	 * @template K
	 * @template V
	 */
	export class Maps<K, V> {

		/**
		 * 筛选 cb => true 的值 如没有 返回 null
		 * @param {(value: T, index: number) => boolean} cb
		 * @returns {T}
		 * @memberof Sets
		 */
		public static filterValue<T>(values: { [key: string]: T }, cb: (value: T, key: string) => boolean): T {
			let key = this.filterKey(values, cb)
			if (!key) {
				return null!
			}
			return values[key]
		}

		/**
		 * 对象的个数
		 * @param value 
		 */
		public static size(value: {}): number {
			let count = 0
			for (let k in value)
				count++
			return count
		}

		/**
		 * 筛选 cb => true 的索引
		 * @param {(value: T, index: number) => boolean} cb
		 * @param isLast 倒着循环
		 * @returns {number}
		 * @memberof Sets
		 */
		public static filterKey<T>(
			values: { [key: string]: T },
			cb: (value: T, key: string) => (boolean | void),
			isLast: boolean = false
		): string {
			if (!isLast) {
				for (let key in values)
					if (cb(values[key], key))
						return key
			}
			else {
				let keys = Maps.keys(values)
				for (let i = keys.length - 1; i >= 0; i--) {
					let key = keys[i]
					if (cb(values[key], key))
						return key
				}
			}
			return null!
		}



		/**取第一个value */
		public static shiftKey<V>(value: { [key: string]: V }): string {
			for (let k in value)
				return k

			return ""
		}

		/**取第一个value */
		public static shiftValue<V>(value: { [key: string]: V }): V {
			for (let k in value)
				return value[k]

			return null!
		}

		/**
		 * 将对象value值转换为数组
		 * @param value 
		 */
		public static converValueArray<V>(value: { [key: string]: V }): V[] {
			let values: V[] = []
			for (let key in value)
				values.push(value[key])
			return values
		}

		/**
		 * 
		 * @param value 数据
		 * @param cb 每次循环回调 返回true终止循环
		 * @param isDeleteChild 循环中删除了子节点也会进入循环
		 */
		public static forEach<V>(value: { [key: string]: V } | Object, cb: (value: V, key: string, index: number) => (boolean | void), isDeleteChild: boolean = false) {
			if (isDeleteChild) {

				let values: V[] = []
				let keys: string[] = []
				// 防止循环删除数据
				for (let k in value) {
					values.push((value as any)[k])
					keys.push(k)
				}
				for (let i = 0; i < values.length; i++) {
					if (!values[i])
						continue
					if (cb)
						if (cb(values[i], keys[i], i))
							break
				}
			}
			else {
				let i = -1
				for (let k in value) {
					i++
					if (!(value as any)[k])
						continue
					if (cb)
						if (cb((value as any)[k], k, i))
							break
				}
			}
		}
		/**随机取一个 V */
		public static randomValue<T>(values: { [key: string]: T }, exclude?: (value: T, key: string) => boolean): T {
			if (this.size(values) == 0)
				return null!
			let res = this.values(values, exclude)
			return res[Maths.zeroToMax(res.length)]
		}

		/**随机取一个 V */
		public static randomKey<T>(values: { [key: string]: T }, exclude?: (value: T, key: string) => boolean): string {
			if (this.size(values) == 0)
				return null!
			let res = this.keys(values, exclude)
			return res[Maths.zeroToMax(res.length)]
		}

		public static values<T>(values: { [key: string]: T }, exclude?: (value: T, key: string) => boolean, out?: T[]): T[] {
			if (out == undefined)
				out = []
			for (let key in values)
				if (exclude) {
					if (!exclude(values[key], key))
						out.push(values[key])
				}
				else
					out.push(values[key])
			return out
		}

		public static map<T, V>(values: { [key: string]: T }, cb: (v: T, key: string) => V): { [key: string]: V } {
			let _value: { [key: string]: V } = {}
			for (let key in values)
				_value[key] = cb(values[key], key)
			return _value
		}

		public static keys<T>(values: { [key: string]: T }, exclude?: (value: T, key: string) => boolean, out?: string[]): string[] {
			if (out == undefined)
				out = []
			for (let key in values)
				if (exclude) {
					if (!exclude(values[key], key))
						out.push(key)
				}
				else
					out.push(key)
			return out
		}

		public static valuesMap<T, R>(values: { [key: string]: T }, cb: (key: string, value: T) => R): R[] {
			let res: R[] = []
			for (let k in values) {
				res.push(cb(k, values[k]) as R)
			}
			return res
		}

		public _values: any = {}

		/**判断key是number类型 */
		public isKeyNumber: boolean = false


		private _onceValue: V = null!
		private _lastKey: K = null!
		private _curKey: K = null!
		public keyArray: K[] = []

		public setCount: number = 0

		get length() {
			return this.keyArray.length
		}

		/**
		 *Creates an instance of Maps.
		 * @param {Object} [obj] 将一个对象转换为 Maps结构
		 * @memberof Maps
		 */
		constructor(obj?: Object) {
			if (obj)
				for (let key in obj)
					this.set(key as any, (obj as any)[key] as any)
		}

		public set(key: K, value: V): Maps<K, V> {
			this.isKeyNumber = (typeof key == "number")
			if (this._curKey != null)
				this._lastKey = this._curKey

			this._curKey = key
			this._values[key] = value
			if (!this._onceValue)
				this._onceValue = value

			Sets.add(this.keyArray, key)
			this.setCount++

			return this
		}

		public has(key: K): boolean {
			let value = this._values[key]
			return !(value == null || value == undefined || value === false)
		}

		public get<T extends V>(key: K): T {
			return this._values[key]
		}

		public gets(keys: K[]): V[] {
			let values: V[] = []
			for (let i = 0; i < keys.length; i++) {
				let value = this.get(keys[i])
				if (value)
					values.push(value)
			}

			return values
		}

		/**通过value 获取添加时的索引 */
		public getValueByIndex(value: V): number {
			return Sets.filterOf(this.keyArray, v => this.get(v) == value)
		}

		public delete(key: K) {
			Sets.delete(this.keyArray, key)
			let value = this._values[key]
			if (value == this._onceValue)
				this._onceValue = null!
			if (this._curKey == key) {
				this._curKey = this._lastKey
				this._lastKey = null!
			}
			delete this._values[key]
		}

		public clear() {
			this.keyArray.length = 0
			this._onceValue = null!
			this._lastKey = null!
			this._curKey = null!
			this._values = {}
			this.setCount = 0
		}

		/**取第一个value */
		public shiftValue<T extends V>(): T {
			if (this._onceValue)
				return this._onceValue as T
			return Maps.shiftValue(this._values)
		}

		/**取上一个value */
		public lastValue(): V {
			return this.get(this._lastKey)
		}

		/**取上当前value */
		public curValue(): V {
			return this.get(this._curKey)
		}

		public converKey(key: string): any {
			return this.isKeyNumber ? Number(key) : key
		}

		/**
		 * 
		 * @param cb 
		 * @param isLast 从后往前便利
		 */
		public forEachArray<T extends V>(cb: (value: T, key: K, index: number) => (boolean | void), isLast: boolean = false) {
			Sets.forEach(this.keyArray, (value, index) => {
				if (cb(this._values[value], value, index))
					return true
				return false
			}, isLast)
		}

		public get size(): number { return Maps.size(this._values) }
		public filterValue<T extends V>(cb: (value: T, key: string) => boolean): T { return Maps.filterValue(this._values, cb) }
		public filterKey<T extends V>(cb: (value: T, key: string) => boolean): K { return Maps.filterKey(this._values, cb) as any as K }
		public randomValue(): V { return Maps.randomValue(this._values) }
		public values<T extends V>(exclude?: (value: T, key: string) => boolean, out?: T[]): T[] { return Maps.values(this._values, exclude, out) }
		public keys(): string[] { return Maps.keys(this._values) }
		public valuesMap<R>(cb: (key: string, value: V) => R): R[] { return Maps.valuesMap(this._values, cb) }
		public forEach<T extends V>(cb: (value: T, key: K) => (boolean | void), isDeleteChild: boolean = false): Maps<K, V> {
			Maps.forEach<T>(this._values, (value, key) => {
				key = this.isKeyNumber ? Number(key) : key as any
				return cb(value, key as any)
			}, isDeleteChild);
			return this
		}

	}
}